﻿using Levfier.GUI.Status;
using Levfier.Proccessors.Parameters;
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;

namespace Levfier.GUI.EditorControls.Parameters
{
    public delegate void DeleteEventHandler(object sender);

    /// <summary>
    /// KnobControl.xaml の相互作用ロジック
    /// </summary>
    [Serializable]
    public partial class KnobControl : UserControl, IParameterHolder, IStatusCreatable
    {
        /// <summary>
        /// IDの連番
        /// </summary>
        private static int totalId = 0;

        /// <summary>
        /// ID
        /// </summary>
        public string Id
        {
            get { return id; }
            set { this.id = value; }
        }

        /// <summary>
        /// ID
        /// </summary>
        private string id;

        /// <summary>
        /// 値変更が行われたときに発生するイベントハンドラ
        /// </summary>
        public event DeleteEventHandler Delete;

        /// <summary>
        /// 編集対象のパラメータ
        /// </summary>
        public ProccessParameter ProccessParameter { get; set; }

        /// <summary>
        /// 最大値
        /// </summary>
        public double MaxValue { get; set; }

        /// <summary>
        /// 最大値
        /// </summary>
        public double MinValue { get; set; }

        /// <summary>
        /// 値設定中か否か
        /// </summary>
        private bool valueChanging;

        /// <summary>
        /// ドラッグの基点
        /// </summary>
        private Point dragOffset;

        /// <summary>
        /// ドラッグ中か否か
        /// </summary>
        private bool dragFlag;

        /// <summary>
        /// ドラッグ開始時のパラメータの値（百分率）
        /// </summary>
        private double dragStartValue;

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public KnobControl()
        {
            InitializeComponent();
            this.valueChanging = false;
            this.id = "M" + DateTime.Now.ToString("yyyyMMddhhmmssfff") + totalId;
            totalId++;
        }

        /// <summary>
        /// ステータスを取得する
        /// </summary>
        /// <returns></returns>
        public ControlStatus GetStatus()
        {
            MainParameterStatus status = new MainParameterStatus();
            status.Name = this.paramName.Text;
            status.Id = this.id;

            status.TargetId = this.ProccessParameter.Id;
            status.MaxValue = this.MaxValue;
            status.MinValue = this.MinValue;
            return status;
        }

        /// <summary>
        /// パラメータを登録する
        /// </summary>
        /// <param name="param"></param>
        public void RegisterParam(ProccessParameter param)
        {
            this.ProccessParameter = param;
            param.ValueChanged += Param_ValueChanged;

            //表示更新
            this.DrawKnob(this.GetValueParcent());
            this.ChangeLabelValue(this.GetValueParcent());
        }

        /// <summary>
        /// ステータスをロードする
        /// </summary>
        /// <param name="param"></param>
        /// <param name="status"></param>
        public void LoadStatus(ProccessParameter param, MainParameterStatus status)
        {
            this.Id = status.Id;
            this.MaxValue = status.MaxValue;
            this.MinValue = status.MinValue;
            this.paramName.Text = status.Name;
            this.RegisterParam(param);
        }

        /// <summary>
        /// ノブをクリックした場合の動作
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Path_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (null != this.ProccessParameter)
            {
                //ノブの百分率値を取得
                double value = GetKnobValue(e.GetPosition(this));

                //描画
                this.DrawKnob(value);

                //パラメータの値を変更
                this.SetValueParcent(value);

                //表示更新
                this.ChangeLabelValue(value);
                this.valueChanging = true;
            }
        }

        /// <summary>
        /// パス上でマウスを移動させた時の動作
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Path_MouseMove(object sender, MouseEventArgs e)
        {
            if (this.valueChanging)
            {
                if (MouseButtonState.Pressed == e.LeftButton)
                {
                    //ノブの百分率値を取得
                    double value = GetKnobValue(e.GetPosition(this));

                    //描画
                    this.DrawKnob(value);

                    //パラメータの値を変更
                    this.SetValueParcent(value);

                    //表示更新
                    this.ChangeLabelValue(value);
                }
                else //左ボタンが離された時
                {
                    this.valueChanging = false;
                }
            }
        }

        /// <summary>
        /// クリックされた場所に合わせてノブを描画
        /// ノブ上の値を百分率で取得する
        /// </summary>
        /// <param name="clickedPoint">クリックした位置</param>
        private double GetKnobValue(Point clickedPoint)
        {
            //ノブの中心
            Point centerPoint = new Point(this.Width / 2.0d, this.Height / 2.0d);
            //クリックされた場所の中心からの角度
            double angle = Math.Atan2(clickedPoint.Y - centerPoint.Y, clickedPoint.X - centerPoint.X);

            //0値点からの相対角度
            double dAngle;
            if (0 == angle)
            {
                dAngle = Math.PI * 3.0d / 2.0d;
            }
            else if (0 > angle)
            {
                dAngle = Math.PI / 2.0d + Math.PI + angle;
            }
            else
            {
                dAngle = angle - Math.PI / 2.0d;
            }

            //値の100分率
            return dAngle / (Math.PI * 3.0d / 2.0d);
        }

        /// <summary>
        /// ノブを描画する
        /// </summary>
        /// <param name="value">0-100</param>
        private void DrawKnob(double value)
        {
            //相対角度
            double dAngle = value * (Math.PI * 3.0d / 2.0d);
            //半径
            double radius = this.arcSeg.Size.Width;

            double angle = dAngle - Math.PI * 3.0d / 2.0d;

            this.arcSeg.IsLargeArc = dAngle > Math.PI;

            this.arcSeg.Point = new Point(radius * Math.Cos(angle) + this.Width / 2.0d, radius * Math.Sin(angle) + this.Height / 2.0d);
        }

        /// <summary>
        /// ラベルの値を更新する
        /// </summary>
        /// <param name="parcent">値（百分率）</param>
        private void ChangeLabelValue(double parcent)
        {
            this.valueLabel.Content = (parcent * 10).ToString("0.0");
        }

        /// <summary>
        /// デリートボタン押下時の動作
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DeleteButton_Click(object sender, RoutedEventArgs e)
        {
            if (null != Delete)
            {
                this.Delete(this);
            }
        }

        /// <summary>
        /// パラメータの現在値を100分率で取得する
        /// </summary>
        /// <returns></returns>
        private double GetValueParcent()
        {
            return (this.ProccessParameter.Value - this.MinValue) / (this.MaxValue - this.MinValue);
        }

        /// <summary>
        /// 百分率の値を実際の値に変換後、パラメータに設定する
        /// </summary>
        private void SetValueParcent(double value)
        {
            this.ProccessParameter.Value = value * (this.MaxValue - this.MinValue) + this.MinValue;
        }

        /// <summary>
        /// パラメータが変化した時の動作
        /// </summary>
        /// <param name="sender"></param>
        private void Param_ValueChanged(object sender)
        {
            //表示更新
            this.DrawKnob(this.GetValueParcent());
            this.ChangeLabelValue(this.GetValueParcent());
        }

        /// <summary>
        /// ドラッグ中動作
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Ellipse_MouseMove(object sender, MouseEventArgs e)
        {
            if (this.dragFlag)
            {
                Point currentPoint = e.GetPosition(this.DragKnob);
                double value = -(currentPoint.Y - this.dragOffset.Y) / 300.0d;
                double dValue = this.dragStartValue + value;

                //リミットする
                if (1 < dValue)
                {
                    dValue = 1;
                }
                if (0 > dValue)
                {
                    dValue = 0;
                }

                //描画
                this.DrawKnob(value);

                //パラメータの値を変更
                this.SetValueParcent(dValue);

                //表示更新
                this.ChangeLabelValue(dValue);
                this.DragKnob.CaptureMouse();
            }
        }

        /// <summary>
        /// ドラッグ開始動作
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Ellipse_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            this.dragFlag = true;
            //摘んだ位置を覚える
            this.dragOffset = e.GetPosition(this.DragKnob);

            this.dragStartValue = this.GetValueParcent();
        }

        /// <summary>
        /// ドラッグ終了動作
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Ellipse_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            this.dragFlag = false;
            this.DragKnob.ReleaseMouseCapture();
        }

    }
}
